//#include <Metro.h>
//#include <Streaming.h>
#include <Arduino.h>
//#include <LiquidCrystal.h>
#include <SPI.h>
#include <Ethernet.h>
#include <SoftwareSerial.h>
//2
//changelog:
//pachube feed in disabled due to instability
//
//disabled calcPower in a trial to eliminate third day freezing
//avrdude -F -v -v -v -pm328p -cstk500v1 -P/dev/ttyACM0  -D -Uflash:w:/tmp/solar_serial2.cpp.hex


//watch out, due to changes in arduino ide, previous versions no longer work
//changes neeeded: server by IP and checking-> client.connect("216.52.233.122", 80)>0)
//removing display in the everlasting fight for stable arduino...

byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};

// initialize the library instance:
EthernetClient client;

EthernetClient trigger_client;

/*
  Simple Solar Battery Monitor
 
 The circuit:
 Volts ( pin0) scaled input from 24vdc battery to max 5v for digitize
 Amps (pin1) input for hall effect current sensor 0 amps ~ 3.5v
 Output to 16x4 LCD display
 
 created Aug 2010 for Battery Monitor
 by Jerry Jeffress
 Adopted Dece 2011 by Petr Vanek petr@linuks.cz
 - 16x4 display
 - pot for display blight and led light instead of contrast/blight pins
 - upload to Pachube
 - remove dependencies on Metro and Streaming
 - DHCP
 
 */
const int voltsInPin = 4; //voltage sensor for 24V system
const int ampsInPin = 5;
const int volts2InPin = 0;


//const int backLitePin = 5; //not used
//const int contrastPin = 6; //not used
const float batterySize = 1.680;  //Battery capacity in KwHrs
const float chargeEff = 0.85;   //Approx Amount of power into a lead acid battery that is stored

//meanwell readings
char mw_acv[4]="000";
char mw_batv[5]="0000";
char mw_load[4]="000";
char mw_temp[5]="0000";
float mw_acv_float;

byte value;


long lastConnectionTime = 0;        // last time you connected to the server, in milliseconds

long lastHttpConnectionTime = 0;        // last time you connected to the server, in milliseconds

long lastVoltageCheckTime = 0;
const int postingInterval = 30000;  //delay between updates to Pachube.com
const int MwInterval = 9000;  //delay between updates to Pachube.com
long lastMwTime = 0;        // last time you connected to the server, in milliseconds


const int VoltageCheckTimeInterval = 1000;  //delay between updates to Pachube.com
long lastConnected = false;      // state of the connection last time through the main loop


//Metro oneSecond = Metro(1000);  // Instantiate an one timer
int aSecond;
int blinkit;
int bStatus;  //Battery status, 0-6 states
float absorbCtr, chargeCtr, disChargeCtr, eqCtr;
float absorbTimeOut = 60*60*3.0;//3 hours in seconds
float eqTimeOut = 60*60*3.0;  //3 Hours in seconds
float tenHours = 60*60*10.0;
float sec2Hr = 1.0/(1000 * 3600.0);  //Convert watt-sec to kwHrs
float volts, volts2, amps, power;

float  bCharge;  //This is power monitor variable total effective charge to and from the battery
float bLow;  //low battery value

//pachube trigger client valules begin here:


int pointer = 0;
unsigned int interval;
#define CONTROL_FEED_ID            42477      // the Pachube feed ID of your dashboard
#define UPDATE_INTERVAL            30000     // if the connection is good wait 10 seconds before updating again - should not be less than 5
#define RESET_INTERVAL             30000     // if connection fails/resets wait 10 seconds before trying again - should not be less than 5
#define PACHUBE_API_KEY            "io3uHUe0A6YnvMdasXo6miXH-3hWu0abCJb24WlD6Aw" // fill in your API key 
byte remoteServer[] = {
  173,203,98,29};   // pachube.com
//char buff[64];
char buff1[64];
char buff2[64];


boolean found_status_200 = false;
boolean found_session_id = false;
boolean found_timestamp  = false;
boolean found_CSV = false;
char *found;
char *pos;
unsigned int successes = 0;
unsigned int failures = 0;
boolean ready_to_update = true;
boolean reading_pachube = false;
boolean request_pause = false;
boolean found_content = false;
unsigned long last_connect;
int content_length;

// const int PachubeInterval = 30000;  //delay between updates to Pachube.com
//boolean lastPachubeConnected = false;      // state of the connection last time through the main loop

int MwRunning = 2;

//pachube trigger client values and here--------------------


// initialize the LCD library with the numbers of the interface pins

void setup(){
  Serial.begin(9600); // Start the Serial communication for debug mainly

  //analogWrite(backLitePin, 64);  //LCD backlite 1/4 full 20.6 ma on battery pack
  //analogWrite(contrastPin, 80); //Set LCD contrast
  //meanwell readings

  aSecond = 0;
  blinkit = 0;
  bCharge = batterySize * 0.95; //init charge 95%  full battery 
  absorbCtr = 0;
  eqCtr = 0;
  chargeCtr = 60*9;
  disChargeCtr = 0;
  bLow = 0.5;  //Low battery warning when hattery 50% charged
  bStatus = 7;//This a an error value must be updated in Calculate bStatus
  // set up the LCD's number of rows and columns: 

  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    //  lcd.setCursor(0,0);
    //  lcd.print("Failed to configure Ethernet using DHCP");
    // lcd.setCursor(0,0);
    //  lcd.print("Failed to configure Ethernet using DHCP");


    // no point in carrying on, so do nothing forevermore:
    for(;;)
    
      ;
  }
  // give the ethernet module time to boot up:
  delay(2000);
// print your local IP address:
  Serial.print("My IP address: ");
  for (byte thisByte = 0; thisByte < 4; thisByte++) {
    // print the value of each byte of the IP address:
    Serial.print(Ethernet.localIP()[thisByte], DEC);
    Serial.print(".");
  }



};

void loop(){

  if (millis() - lastConnectionTime > postingInterval){ //30000
       
        readMeanwell();
        sampleVoltsAmps();
        sendData(); //data sending happens here
  
  }
}

void sampleVoltsAmps() {
  float voltSum = 0.0;
  float ampSum = 0.0;
  float voltSum2 = 0.0;

  // loop 10 time over one 50hz cycle to smooth
  for (int i = 0; i < 10; i++){
    //volts = map(analogRead(voltsInPin), 0, 1023, 0, 6); //adjust for your sensor
    volts=(analogRead(4)/13.58); //12.99
    voltSum = voltSum + volts;
    //amps = map(analogRead(ampsInPin), 0, 1023, 0, 89.4); //adjust for your sensor

    volts2=(analogRead(0)/19.17); //12.99
    voltSum2 = voltSum2 + volts2;


    amps=(analogRead(5)/7.4);
    ampSum = ampSum + amps;
    delay(2);
  }//end smooth loop
  //Get 10 sample average
  volts = voltSum /10.0;  //Factor to scale to volts
  volts2 = voltSum2 /10.0;  //Factor to scale to volts
//  volts2 = freemem();  
  //extra battery checking replaced by
  //free memory function to check the reason
  //for disrupted data logging
  

  amps = ampSum /10.0;  //Factor to scale to Amps

}

//Calc power
void calcPower(){
  power =  volts * amps;  //Units are watt-seconds
  if (power <= 0){
    bCharge = bCharge +(power * sec2Hr);
  }
  else {
    bCharge = bCharge + (power * sec2Hr * chargeEff);
  }
}
//Calculate bStatus 
// New Day


// this method makes a HTTP connection to the server:
void sendData() {
  // if there's a successful connection:
  if (client.connect("216.52.233.122", 80)>0) {
    //    Serial.print("connecting and sending...status:");
    //    Serial.println(client.connected());
    //    Serial.print("mapped volts pin: ");
    //    Serial.println(fmap(analogRead(voltsInPin), 0, 675.84, 0, 51.81));
    //    Serial.print("raw: ");
    //    Serial.println(analogRead(voltsInPin));
    //    Serial.print("13.05: ");
    //    Serial.println(analogRead(voltsInPin)/13.044);
    //    


    // send the HTTP PUT request. 
    // fill in your feed address here:
    client.print("PUT /api/41032.csv HTTP/1.1\n");
    client.print("Host: www.pachube.com\n");
    // fill in your Pachube API key here:
    client.print("X-PachubeApiKey: ioTPoIarBUKZV-w0XFc4CFKObrY4fER4u4vVbvUAajw\n");
    client.print("Content-Length: ");

    //int thisData = analogRead(5);
    //float thisData = volts;

    //String dataString = String(volts);

    //dataString += ",";
    //dataString += String(amps);

    mw_acv_float = atoi(mw_acv); //convert char to float, acv will be sent /10
    //Serial.print("acv: ");
    //Serial.println(mw_acv);
    //Serial.println(mw_acv_float/10);

    int thisLength = getLength(volts)+3; //3 are floats
    thisLength = thisLength + getLength(amps)+3; //3 are floats
    thisLength = thisLength + getLength(mw_acv_float/10)+3; //3 are floats

    thisLength = thisLength + getLength(volts2)+3; //3 are floats

    //  thisLength = thisLength + 3; // mw_acv

    thisLength = thisLength + 4; // mw_batv
    thisLength = thisLength + 3; // mw_load
    thisLength = thisLength + 4; // mw_temp

    thisLength = thisLength + 5; // 5x separator ","




    // calculate the length of the sensor reading in bytes:
    //client.println(dataString.length(), DEC);
    client.println(thisLength, DEC); 
    //client.println(11); 

    //+3 is an ugly hack to overcome float length calculation issues (,00)

    //Serial.print("length:  "); 
    //Serial.println(thisLength + 2);  
    //    Serial.print("volts: ");
    //    Serial.println(volts);
    //    Serial.print("amps in: ");
    //    Serial.println(amps);  

    // last pieces of the HTTP PUT request:
    client.print("Content-Type: text/csv\n");
    client.println("Connection: close\n");

    // here's the actual content of the PUT request:
    //client.print("1, ");
    client.print(volts);
    client.print(",");
    client.print(amps);

    client.print(",");
    client.print(mw_acv_float/10);

    client.print(",");
    client.print(mw_batv);

    client.print(",");
    client.print(mw_load);

    client.print(",");
    client.print(mw_temp); //last line with \n

    client.print(",");    
    client.println(volts2);




    //mw_acv, mw_batv, mw_load


    //client.println("28.57, 0.95");

    Serial.print("sending data: "); 

    Serial.print(volts);
    Serial.print(",");
    Serial.print(amps);

    Serial.print(",");
    Serial.print(mw_acv_float/10);

    Serial.print(",");
    Serial.print(mw_batv);

    Serial.print(",");
    Serial.print(mw_load);

    Serial.print(",");
    Serial.print(mw_temp);

    Serial.print(",");
    Serial.println(volts2);

    //    Serial.print(volts);
    //    Serial.print(", ");
    //    Serial.println(amps);
    //client.println(dataString);

    // note the time that the connection was made:
    lastConnectionTime = millis();
    //    Serial.println("read response");
    //    Serial.println("disconnecting.");
    
 
    
    client.stop();

  } 
  else {
    // if you couldn't make a connection:
        Serial.println("connection failed");
  }
}


// This method calculates the number of digits in the
// sensor reading.  Since each digit of the ASCII decimal
// representation is a byte, the number of digits equals
// the number of bytes:

int getLength(int someValue) {
  // there's at least one byte:
  int digits = 1;
  // continually divide the value by ten, 
  // adding one to the digit count for each
  // time you divide, until you're at 0:
  int dividend = someValue /10;
  while (dividend > 0) {
    dividend = dividend /10;
    digits++;
  }
  // return the number of digits:
  return digits;
}


float fmap(float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

void readMeanwell()
{
  //inCount = 0;
  //  memset(mw_acv, '0', sizeof(mw_acv));
  // memset(mw_batv, 0, sizeof(mw_batv));
  //  memset(mw_load, '0', sizeof(mw_load));
  //    memset(mw_temp, 0, sizeof(mw_temp));

  mw_acv[0]='0';
  mw_acv[1]='0';
  mw_acv[2]='0';

  mw_batv[0]='0';
  mw_batv[1]='0';
  mw_batv[2]='.';
  mw_batv[3]='0';

  mw_load[0]='0';
  mw_load[1]='0';
  mw_load[2]='0';

  mw_temp[0]='0';
  mw_temp[1]='0';
  mw_temp[2]='.';
  mw_temp[3]='0';

  //Serial.print("debug temp: ");
  //Serial.println(mw_temp);

  //initialize soft serial on pins
  //must happen each time to be able to find out that
  //mw was turned on/off

  SoftwareSerial mySerial(7, 6);

  mySerial.begin(9600); //start soft serial port

  value = 0;
  mySerial.print("Q\r"); //send the Q query
  delay(10);

  Serial.println("requesting data from meanwell");
  delay(100);
  //response will be like:
  //(226 025 26.1 100 32.0 002 50.0 388 006 1000000000000000000)
  //0123456789


  if (mySerial.available() > 0  ) 
  {
    value=mySerial.read(); // intro (
    if (value==40) // if string starts with (
    {
      mw_acv[0]=mySerial.read(); //AC volt
      mw_acv[1]=mySerial.read(); //AC volt
      mw_acv[2]=mySerial.read(); //AC volt

      value=mySerial.read(); //space

      mw_load[0]=mySerial.read();
      mw_load[1]=mySerial.read();
      mw_load[2]=mySerial.read();
      value=mySerial.read(); //space

      mw_batv[0]=mySerial.read();
      mw_batv[1]=mySerial.read();
      mw_batv[2]=mySerial.read();
      mw_batv[3]=mySerial.read();

      value=mySerial.read(); //space

      value=mySerial.read(); //battery value
      value=mySerial.read(); //battery value
      value=mySerial.read(); //battery value
      value=mySerial.read(); //space

      mw_temp[0]=mySerial.read();
      mw_temp[1]=mySerial.read();
      mw_temp[2]=mySerial.read();
      mw_temp[3]=mySerial.read();

      mySerial.flush();

    }
  }
  //lastMwTime=millis();
  mySerial.end();

}




void pachube_in(){

  if (millis() < last_connect) last_connect = millis();

  if (request_pause){
    if ((millis() - last_connect) > interval){
      ready_to_update = true;
      reading_pachube = false;
      request_pause = false;
      found_status_200 = false;
      found_session_id = false;
      found_CSV = false;

      //Serial.print("\nReady to connect: ");
      //Serial.println(millis());
    }
  }

  if (ready_to_update){
    //Serial.println("\nConnecting...");

    if (trigger_client.connect("www.pachube.com", 80)) {


      Serial.println("requesting trigger data");

      trigger_client.print("GET /v2/feeds/");
      trigger_client.print(CONTROL_FEED_ID);
      trigger_client.print(".csv HTTP/1.1\nHost: api.pachube.com\nX-PachubeApiKey: ");
      trigger_client.print(PACHUBE_API_KEY);
      trigger_client.print("\nUser-Agent: Arduino (Pachube controller v1.1)");
      trigger_client.print("\nConnection: close");
      trigger_client.println("\n");

      ready_to_update = false;
      reading_pachube = true;
      request_pause = false;
      interval = UPDATE_INTERVAL;

    } 
    else {
      Serial.print("connection failed!");
      Serial.print(++failures);
      found_status_200 = false;
      found_session_id = false;
      found_CSV = false;
      ready_to_update = false;
      reading_pachube = false;
      request_pause = true;
      last_connect = millis();
      interval = RESET_INTERVAL;
      //setupEthernet();
    }
  }

  while (reading_pachube){
    while (trigger_client.available()) {
      checkForResponse();
    } 

    if (!trigger_client.connected()) {
      disconnect_pachube();
    }
  } 
}

void disconnect_pachube(){
  //Serial.println("Disconnecting");
  trigger_client.stop();
  ready_to_update = false;
  reading_pachube = false;
  request_pause = true;
  last_connect = millis();
  found_content = false;
  //resetEthernetShield();
}



void checkForResponse(){  

  char c = trigger_client.read();
  //Serial.print(c);
  buff1[pointer] = c;

  if (pointer < 64) 
    pointer++;


  if (c == '\n') {

    found = strstr(buff1, "200 OK");
    if (found != 0){
      found_status_200 = true; 
      //Serial.println("Status 200 found");
    }



    buff1[pointer]=0;
    found_content = true;

    strcpy(buff2, buff1);
    clean_buffer(); 
    /*  Serial.print("status200buff2 : ");
     Serial.println(buff2);   */

    if (found_status_200){
      //timestamp
      found = strstr(buff2, "Vary:");
      //Serial.print("searching in: ");
      //Serial.print(buff1);
      if (found != 0){
        //char csvLine[strlen(buff1)+0];
        //strncpy (csvLine,buff1,strlen(buff1)+0); 
        //clean_buffer();
        found_timestamp = true; 
        //Serial.print("statustimestamp_csv : ");
        //Serial.println(buff1); 
      }
    }
  }


  if ((found_timestamp) && (!found_CSV) && (strlen(buff1)>=31)){

    //Serial.println(buff1);
    found = strstr(buff1, ",");
    if (found != 0){
      //buff2
      char csvLine[strlen(buff1)];
      strncpy (csvLine,buff1,strlen(buff1));

      //Serial.println("CSV: ");    
      int g=0;
      for (g=0;g<31;g++){ 
        //Serial.print(g);
        //Serial.println(csvLine[g]);

      }
      //Serial.println();
      //Serial.println("end");  

      //Serial.println(csvLine[32]);

      int counter = 0;


      if(csvLine[30]=='1'){
        if (MwRunning!=1){ //if not running or unknown, turn ON

          SoftwareSerial ControlSerial(7, 6);
          ControlSerial.begin(9600); //start soft serial port
          ControlSerial.print("C010000000000000\r"); //send the ON command
          delay(10);
          ControlSerial.end();

          Serial.println("ON");
          trigger_client.stop();

        }
        MwRunning=1;
      }
      else if (csvLine[30]=='0'){
        if (MwRunning!=0){ // if running or unknown, turn OFF
          SoftwareSerial ControlSerial(7, 6);
          ControlSerial.begin(9600); //start soft serial port
          ControlSerial.print("C100000000000000\r"); //send the OFF command
          delay(10);
          ControlSerial.end();
          Serial.println("OFF");  
          trigger_client.stop();


        }
        MwRunning=0;

      }

      found_CSV = true;


    }
  }


}

void clean_buffer() {
  pointer = 0;
  memset(buff1,0,sizeof(buff1)); 
}


int availableMemory() {
  int size = 2048; // Use 2048 with ATmega328
  byte *buf;

  while ((buf = (byte *) malloc(--size)) == NULL)
    ;

  free(buf);

  return size;
}

extern int  __bss_end;
extern int  *__brkval;
int freemem(){
 int free_memory;
 if((int)__brkval == 0)
   free_memory = ((int)&free_memory) - ((int)&__bss_end);
 else
   free_memory = ((int)&free_memory) - ((int)__brkval);
 return free_memory;
} 

